﻿using System;
using System.Collections.Generic;
using log4net;
#if USE_MARSHALING
using OdsServer.DataTransfer.Marshalling;
#endif
#if USE_PIPES
using OdsServer.DataTransfer.Pipes;
#endif
using OdsServer.DataTransfer.Sockets;
using OdsServer.Common;
using OdsServer.Common.DataTransfer;
using OdsServer.Common.Packet;

namespace OdsServer.DataTransfer
{
    internal delegate void DataPublishRequestDelegate(object sender, IBasePacket data);

    internal sealed class TransferService : ITransferService
    {
        private static readonly ILog log                        = LogManager.GetLogger(typeof(TransferService));
        private List<IDataPublisherAgent> publisherList         = new List<IDataPublisherAgent>();
        private event DataPublishRequestDelegate OnSendRequest  = null;

        public TransferService()
        {
            log.Info("OdsServer instance created");
        }

        ~TransferService()
        {
            this.Stop();
        }
        
        public bool IsValid
        {
            get { return true; }
        }

        public SetupParamChangedCallback SetupParamChangedCallback
        {
            get
            {
                return new SetupParamChangedCallback(this.OnSetupParamChanged);
            }
        }

        public void Start()
        {
            lock (this)
            {
                this.OnSendRequest = null;

                foreach (IDataPublisherAgent publisher in this.publisherList)
                {
                    // send request linking
                    this.OnSendRequest += new DataPublishRequestDelegate(publisher.Publish);

                    // fault callback linking
                    publisher.OnFault += new DataPublisherFaultDelegate(this.ProcessPublisherFault);

                    publisher.StartAgent();

                    log.InfoFormat("Publisher {0} associated with server and started.", publisher.ToString());
                }
            }
        }

        public void Stop()
        {
            lock (this)
            {
                this.OnSendRequest = null;

                foreach (IDataPublisherAgent publisher in this.publisherList)
                {
                    publisher.StopAgent();

                    log.InfoFormat("Publisher {0} stopped.", publisher.ToString());
                }
            }
        }

        public void ProcessData(IBasePacket data)
        {
            if (data != null)
            {
                try
                {
                    if (OnSendRequest != null)
                    {
                        OnSendRequest(this, data);
                    }
                }
                catch (Exception e)
                {
                    log.DebugFormat("Error while sending data: {0}", e.Message);
                }
            }
            else
                throw new DataTransferException("Data object is invalid.");
        }

        private void ProcessPublisherFault(object sender, DataTransferException e)
        {
            log.DebugFormat(
                    "Publisher fault registered by {0}: {1}",
                    sender.ToString(),
                    e.Message );
        }

        private void Init(SetupParameters parameters)
        {
            lock (this.publisherList)
            {
                this.publisherList.Clear();

                if (parameters.usePipeTransfer)
                {
#if USE_PIPES
                    this.publisherList.Add(new PipeTransfer());

                    log.Info("PipeTransfer added to publisher list.");
#else
                    log.Error("PipeTransfer not available.");
#endif
                }
                if (parameters.useSocketTransfer)
                {
                    this.publisherList.Add(new SocketTransfer(OdsServerConfiguration.GetInstance()));

                    log.Info("SocketTransfer added to publisher list.");
                }
                if (parameters.useMarshalTransfer)
                {
#if USE_MARSHALING
                    this.publisherList.Add(new MarshalTransfer());

                    log.Info("MarshalTransfer added to publisher list.");
#else
                    log.Error("MarshalTransfer not available");
#endif
                }
            }
        }

        private void OnSetupParamChanged(SetupParameters parameters)
        {
            lock (this)
            {
                if (this.OnSendRequest != null)
                {
                    this.Stop();

                    this.Init(parameters);

                    this.Start();
                }
                else
                    this.Init(parameters);
            }
        }
    }
}